/**
 * qiao.canvas.js
 */
define(function (require, exports, module) {
    'use strict';
	
    var qiao = require('qiao.util.js');
    
    /**
     * 获取canvas，并设置宽高 
     */
    exports.canvas = function(id, width, height){
    	var canvas = document.getElementById(id);
    	if(canvas){
    		if(width) canvas.width = width;
    		if(height) canvas.height = height;
    	}
    	
    	return canvas;
    };
    
    /**
     * 获取2d对象 
     */
    exports.ctx = function(c, w, h){
    	var canvas = (typeof c == 'string') ? exports.canvas(c, w, h) : c;
    	var ctx = (canvas && canvas.getContext) ? canvas.getContext('2d') : null;
    	
    	return ctx;
    };
    
    /**
     * 设置文字 
     */
    exports.text = function(options){
    	if(!options) return;
    	
    	// ctx
    	var c = options.c || exports.c;
    	if(!c) return;
    	
    	c.font = options.f || '10px sans-serif';
    	c.textAlign = options.ta || 'start';
    	c.textBaseline = options.tb || 'alphabetic';
    	c.direction = options.d || 'inherit';
    	
    	if(options.m == 'stroke') c.strokeText(options.t, options.x, options.y);
    	if(options.m == 'fill') c.fillText(options.t, options.x, options.y);
    };

    /**
     * 设置文字-stroke
     */
    exports.texts = function(options){
    	if(!options) return;
    	
    	options.m = 'stroke';
    	exports.text(options);
    };
    
    /**
     * 设置文字-fill
     */
    exports.textf = function(options){
    	if(!options) return;
    	
    	options.m = 'fill';
    	exports.text(options);
    };
    
    /**
	 * http://cubic-bezier.com/#.62,.06,.59,1.56
	 */
	// These values are established by empiricism with tests (tradeoff: performance VS precision)
	var NEWTON_ITERATIONS = 4;
	var NEWTON_MIN_SLOPE = 0.001;
	var SUBDIVISION_PRECISION = 0.0000001;
	var SUBDIVISION_MAX_ITERATIONS = 10;
	
	var kSplineTableSize = 11;
	var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
	
	var float32ArraySupported = typeof Float32Array === 'function';
	
	function A(aA1, aA2) {
	    return 1.0 - 3.0 * aA2 + 3.0 * aA1;
	}
	
	function B(aA1, aA2) {
	    return 3.0 * aA2 - 6.0 * aA1;
	}
	
	function C(aA1) {
	    return 3.0 * aA1;
	}
	
	// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
	function calcBezier(aT, aA1, aA2) {
	    return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
	}
	
	// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
	function getSlope(aT, aA1, aA2) {
	    return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
	}
	
	function binarySubdivide(aX, aA, aB, mX1, mX2) {
	    var currentX, currentT, i = 0;
	    do {
	        currentT = aA + (aB - aA) / 2.0;
	        currentX = calcBezier(currentT, mX1, mX2) - aX;
	        if (currentX > 0.0) {
	            aB = currentT;
	        } else {
	            aA = currentT;
	        }
	    } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
	    return currentT;
	}
	
	function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
	    for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
	        var currentSlope = getSlope(aGuessT, mX1, mX2);
	        if (currentSlope === 0.0) {
	            return aGuessT;
	        }
	        var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
	        aGuessT -= currentX / currentSlope;
	    }
	    return aGuessT;
	}
	
	
	/**
	 * 贝塞尔曲线生成器
	 * @param mX1
	 * @param mY1
	 * @param mX2
	 * @param mY2
	 * @returns {Function}
	 */
	exports.easing = function bezier(mX1, mY1, mX2, mY2) {
	    if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
	        throw new Error('bezier x values must be in [0, 1] range');
	    }
	
	    // Precompute samples table
	    var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
	    if (mX1 !== mY1 || mX2 !== mY2) {
	        for (var i = 0; i < kSplineTableSize; ++i) {
	            sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
	        }
	    }
	
	    function getTForX(aX) {
	        var intervalStart = 0.0;
	        var currentSample = 1;
	        var lastSample = kSplineTableSize - 1;
	
	        for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
	            intervalStart += kSampleStepSize;
	        }
	        --currentSample;
	
	        // Interpolate to provide an initial guess for t
	        var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
	        var guessForT = intervalStart + dist * kSampleStepSize;
	
	        var initialSlope = getSlope(guessForT, mX1, mX2);
	        if (initialSlope >= NEWTON_MIN_SLOPE) {
	            return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
	        } else if (initialSlope === 0.0) {
	            return guessForT;
	        } else {
	            return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
	        }
	    }
	
	
	    /**
	     * 缓冲函数
	     * @param x {Number} 时间占比，[0, 1]
	     * @returns {Number} 过程占比，[0, 1]
	     */
	    return function bezierEasing(x) {
	        if (mX1 === mY1 && mX2 === mY2) {
	            return x; // linear
	        }
	
	        // Because JavaScript number are imprecise, we should guarantee the extremes are right.
	        if (x === 0) {
	            return 0;
	        }
	
	        if (x === 1) {
	            return 1;
	        }
	
	        return calcBezier(getTForX(x), mY1, mY2);
	    };
	};
});